package com.apes.scm.rbac.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.apes.framework.api.DomainService;
import com.apes.framework.api.SpringManager;
import com.apes.framework.jpa.generator.service.SequenceService;
import com.apes.framework.jpa.spec.JpaDsl;
import com.apes.framework.jpa.spec.model.Group;
import com.apes.framework.plugin.cache.Cache;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.framework.util.MapUtil;
import com.apes.framework.util.PinyinUtil;
import com.apes.scm.masterdata.enumeration.model.Enumeration;
import com.apes.scm.rbac.model.Menu;
import com.apes.scm.rbac.model.MenuAuthItem;
import com.apes.scm.rbac.model.Operation;
import com.apes.scm.rbac.repository.MenuRepository;
import org.hibernate.query.internal.NativeQueryImpl;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.*;

/**
 * 功能：菜单服务
 *
 * @author xul
 * @create 2018-03-05 10:50
 */
@Service("menuService")
public class MenuService extends DomainService {
    private final String CONST_MENU_CACHE = "menuCache";
    private final String CONST_MENU_KEY = "menu";
    private final String CONST_MENU_AUTH_CACHE = "menuAuthCache";
    @Autowired
    private Cache cache;

    @Autowired
    private MenuRepository menuRepository;

    @Autowired
    private EntityManager em;

    @Autowired
    private SequenceService sequenceService;

    @Autowired
    private RbacDao rbacDao;

    public Menu save(SimpleRequest request) {
        Menu menu = request.getO(Menu.class);
        menu.setDevelopUsers(menu.getDevelopUserList() != null ? menu.toDevelopUser(menu.getDevelopUserList()) : null );
        if (menu.getId() == null || menu.getId() == 0){
            Integer id = Integer.valueOf(sequenceService.getId(Menu.CONST_TABLE_NAME));
            menu.setId(id);
        }
        menu.setPath(menu.localPath());
        menu.setInitials(PinyinUtil.changeToTonePinYin(menu.getName()).replaceAll(" ", "").replaceAll(" ", ""));
        menu = menuRepository.saveAndFlush(menu);
        addMenu(menu);
        return menu;
    }

    public Menu update(SimpleRequest request) {
        Menu menu = request.getO(Menu.class);
        menu.setDevelopUsers(menu.getDevelopUserList() != null ? menu.toDevelopUser(menu.getDevelopUserList()) : null );
        menu.setPath(menu.localPath());
        Boolean oldValid = menuRepository.findOne(menu.getId()).isValid();
        menu.setInitials(PinyinUtil.changeToTonePinYin(menu.getName()).replaceAll(" ", ""));
        try {
            menu = menuRepository.saveAndFlush(menu);
        }catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(String.format("菜单：[ %s ] 编辑异常！请检查按钮是否已被使用！ %s", menu.getId(), e.getCause()));
        }
        addMenu(menu);
        clearMenuAuthCache(menu, oldValid);
        return menu;
    }

    public String delete(SimpleRequest request) {
        Menu menu = request.getO(Menu.class);
        menuRepository.delete(menu);
        Map<Integer, Menu> menus = getMenus();
        menus.remove(menu.getId());
        cache.put(CONST_MENU_CACHE, CONST_MENU_KEY, menus);
        return "删除成功";
    }

    private void addMenu(Menu menu) {
        menu.getGroupType().getCode();
        menu.getTerminalType().getCode();
        Map<Integer, Menu> menus = getMenus();
        if (menu.isValid())
            menus.put(menu.getId(), menu);
        cache.put(CONST_MENU_CACHE, CONST_MENU_KEY, menus);
    }

    public Map<Integer, Menu> getMenus(){
        String CACHE_INIT_FLAG = "initFlag";
        Boolean init = (Boolean) cache.get(CONST_MENU_CACHE, CACHE_INIT_FLAG);
        if (init == null || !init) {
            Map<Integer, Menu> menus = menuRepository.findAll().stream()
                    .filter(e -> e.isValid()).map(menu -> {
                menu.getGroupType().getCode();
                menu.getTerminalType().getCode();
                return menu;
            }).collect(Collectors.toMap(Menu::getId, menu -> menu));
            cache.put(CONST_MENU_CACHE, CONST_MENU_KEY, menus);
            cache.put(CONST_MENU_CACHE, CACHE_INIT_FLAG, true);
        }
        return (Map<Integer, Menu>) cache.get(CONST_MENU_CACHE, CONST_MENU_KEY);
    }

    public List<Map> findTreeData() {
        return menuRepository.findTreeData();
    }

    /**
     * 根据分组和终端类型查找
     *
     * @param request
     * @return
     */
    public List<Map> findTreeDataByGroupAndTerminal(SimpleRequest request) {
        List<Map<String, Object>> condition = request.get("condition");
        Map<String, Object> map = new HashMap<>();
        condition.forEach(c -> {
            map.put(c.get("field").toString(), c.get("value"));
        });
        List<Map> treeDataByGroupAndTerminal = null;
        if (map.get("developUsers") != null) {
            treeDataByGroupAndTerminal = SpringManager.getBean(RbacDao.class).findTreeDataByGroupAndTerminalAndDAndDevelopUsers(map);
        }
        else treeDataByGroupAndTerminal = menuRepository.findTreeDataByGroupAndTerminal(map.get("group").toString(), map.get("terminal").toString());
        return treeDataByGroupAndTerminal;
    }

    public List<Menu> importMenus(SimpleRequest request) {
        List<Map> rows = request.getList();
        List<Menu> menus = new ArrayList<>();
        rows.stream().forEach((row) -> {
            menus.addAll(getMenus(null, row));
        });
        menuRepository.save(menus);
        return menus;
    }

    private List<Menu> getMenus(Menu parent, Map row) {


        List<Menu> menus = new ArrayList<>();
        Menu menu = new Menu();
        menu.setId(Integer.valueOf(getBean(SequenceService.class).getId(Menu.CONST_TABLE_NAME)));
        if (row.containsKey("text")) {
            menu.setName((String) row.get("text"));
        }
        if (row.containsKey("flowId")) {
            menu.setFlowId((String) row.get("flowId"));
        }
        if (row.containsKey("icon")) {
            menu.setIcon((String) row.get("icon"));
        }
        if (row.containsKey("link")) {
            menu.setLink((String) row.get("link"));
        }
        if (row.containsKey("terminal")) {
            menu.setTerminalType((Enumeration) row.get("terminalType"));
        }
        if (row.containsKey("translate")) {
            menu.setTranslate((String) row.get("translate"));
        }
        if (row.containsKey("group")) {
            menu.setGroup((Boolean) row.get("group"));
        }
        menu.setParent(parent);
        menus.add(menu);
        if (row.containsKey("children")) {
            List<Map> rows = (List<Map>) row.get("children");
            rows.stream().forEach((item) -> {
                menus.addAll(getMenus(menu, item));
            });
        }
        return menus;
    }


    public List<Map> getMenus(List<Menu> rows) {
        /**获取菜单树**/
      // Map<Integer, Menu> menus = getTreeMenu(rows);

        Map<Integer, Menu> allMenus = getMenus();
        Map<Integer, Menu> menus = getMenus(allMenus, rows);

        /**将菜单树转成前端需要的格式**/
        List<Map> results = new ArrayList<>();
        menus.forEach((id, menu) -> {
            Map m = getMenu(menu); //, "groupType", "terminalType"
            results.add(m);
        });
        sort(results);
        return results;
    }

    private Map<Integer, Menu> getMenus(Map<Integer, Menu> allMenus, List<Menu> authMenus){
        Map<Integer, Menu> menus = new HashMap<>();
        authMenus.forEach(item -> {
            /**如果菜单权限没有定义到末级，可以先在此遍历末级**/
            /**遍历父级节点，取根节点**/
            Menu menu = getParent(allMenus, item);
            Integer menuId = menu.getId();
            if (!menus.containsKey(menuId)) {
                menus.put(menuId, menu);
            }
        });
        return menus;
    }

    private Menu getParent(Map<Integer, Menu> allMenus, Menu menu) {
        String parentPath = substring(menu.getPath(), "/");
        if(parentPath == null) return menu;
        Menu parent = allMenus.values().stream().filter(m -> m.getPath().equals(parentPath)).findFirst().orElse(null);
        if(parent == null) throw new RuntimeException("菜单：" + menu.getId() + " 没有找到上级菜单！");
        Map<Integer, Menu> children = parent.getMenus();
        if (children == null) children = new HashMap<>();
        children.put(menu.getId(), menu);
        parent.setMenus(children);
        if (parent.getParent() != null) {
            parent = getParent(allMenus, parent);
        }
        return parent;
    }

    private String substring(String toStr, String delimiter) {
        int lastIndex  = toStr.lastIndexOf(delimiter);
        return lastIndex < 0 ?  null: toStr.substring(0, lastIndex);
    }

    private Map<Integer, Menu> getTreeMenu(List<Menu> rows) {
        Map<Integer, Menu> menus = new HashMap<>();
        rows.forEach(item -> {
            /**如果菜单权限没有定义到末级，可以先在此遍历末级**/
            /**遍历父级节点，取根节点**/
            Menu menu = getParent(item);
            Integer menuId = menu.getId();
            if (!menus.containsKey(menuId)) {
                menus.put(menuId, menu);
            }
        });
        return menus;
    }

    /**
     * 功能：菜单转成Map，含子菜单
     */
    private Map getMenu(Menu menu, String... excludeFields) {
        Map m = getMap(menu, excludeFields);
        if (menu.getMenus() != null) {
            List<Map> children = new ArrayList<>();
            menu.getMenus().forEach((id, child) -> {
                children.add(getMenu(child, excludeFields));
            });
            /**在此可以对子节点进行排序**/
            sort(children);
            m.put("children", children);
        }
        return m;
    }

    /**
     * 功能：对子节点进行排序
     */
    private void sort(List<Map> menus) {
        Collections.sort(menus, (o1, o2) -> {
            if (((String) o1.get("sortCode")).compareTo((String) o2.get("sortCode")) > 0) {
                return 1;
            }
            if (((String) o1.get("sortCode")).compareTo((String) o2.get("sortCode")) == 0) {
                return 0;
            }
            return -1;
        });
    }

    /**
     * 功能：菜单转成Map
     */
    private Map getMap(Menu menu, String... excludeFields) {
        Map result = MapUtil.mapper("id", menu.getId(),
                "group", menu.isGroup(),
                "combinationMenu",menu.isCombinationMenu(),
                "text", menu.getName(),
                "translate", menu.getTranslate(),
                "link", menu.getLink(),
                "icon", menu.getIcon(),
                "sortCode", menu.getSortCode(),
                "groupType", menu.getGroupType(),
                "terminalType", menu.getTerminalType(),
                "initials", menu.getInitials(),
                "operations", new ArrayList(menu.getAuthOperations().values()));
        if(excludeFields != null){
            for (String excludeField : excludeFields) {
                result.remove(excludeField);
            }
        }
        return result;
    }

    /**
     * 功能：取菜单的根菜单
     */
    private Menu getParent(Menu menu) {
        Menu parent = menu.getParent();
        if (parent == null) {
            parent = menu;
        } else {
            Map<Integer, Menu> children = parent.getMenus();
            if (children == null) {
                children = new HashMap<>();
            }
            children.put(menu.getId(), menu);
            parent.setMenus(children);
        }
        if (parent.getParent() != null) {
            parent = getParent(parent);
        }
        return parent;
    }

    /**
     * 功能：菜单转成Map，含子菜单
     */
    private Map getTreeMenuMap(Menu menu) {
        Map m = getTreeMap(menu);
        if (menu.getMenus() != null) {
            List<Map> children = new ArrayList<>();
            menu.getMenus().forEach((id, child) -> {
                // 增加判断菜单是否有效，设置父级其下面的子菜单不显示
                if (child.isValid()) {
                    children.add(getTreeMenuMap(child));
                }
            });
            menu.getOperations().forEach((child) -> {
                children.add(getTreeOperationsMap(child));
            });
            /**在此可以对子节点进行排序**/
            sort(children);
            m.put("children", children);
        }
        return m;
    }

    /**
     * 功能：转成树形组件Map
     */
    private Map getTreeMap(Menu menu) {
        return MapUtil.mapper("id", menu.getId(),
                "parent", menu.getParent() != null ? menu.getParent().getId() : "",
                "data", menu.getId(),
                "label", menu.getName(),
                "sortCode", menu.getSortCode()
        );
    }

    private Map getTreeOperationsMap(Operation operation) {
        return MapUtil.mapper("id", operation.getMenu().getId() + "," + operation.getOperationId(),
                "parent", operation.getMenu().getId(),
                "data", operation.getOperationId(),
                "label", operation.getName(),
                "leaf", true,
                "sortCode", "A"
        );
    }

    /*
     * 树形控件 循环递归
     * menus
     * **/
    public List<Map> getAllMenu(SimpleRequest request) {


        List<Menu> menusList = menuRepository.findAll();
        Map<Integer, Menu> menuMap = menusList.stream().collect(toMap(Menu::getId, (menu) -> menu));
//        System.out.println("request = [" + menuMap + "]");
        IntSummaryStatistics menuStatistics = menusList.stream().collect(summarizingInt(Menu::getId));
//        System.out.println("rows = [" + menuStatistics.getCount() + "]");
//        System.out.println("rows = [" + menuStatistics.getMax() + "]");
//        System.out.println("rows = [" + menuStatistics.getMin() + "]");
//        System.out.println("rows = [" + menuStatistics.getAverage() + "]");
        Map<Integer, Menu> menus = getTreeMenu(menusList);
        /**将结果转成tree组件对应结果**/
        List<Map> results = new ArrayList<>();
        menus.forEach((id, menu) -> {
            results.add(getTreeMenuMap(menu));
        });
        sort(results);
        return results;
    }

    // 根据 oneToMany 关系递归 速度最慢
    public List<Map> getAllMenu1(SimpleRequest request) {
        // 找出root根节点
        List<Menu> menusList = menuRepository.findAllByParentIsNullOrderBySortCodeAsc();
        List<Map> results = new ArrayList<>();

        menusList.forEach(
                (menu) -> {
                    Map result = getTreeMap(menu);
                    List<Map> childrent = addChildrent(menu);
                    result.put("children", childrent);
                    results.add(result);
                }
        );
        sort(results);
        return results;
    }

    private List<Map> addChildrent(Menu menu) {
        List<Map> list = new ArrayList<>();
        if (menu.getChildren() != null) {
            menu.getChildren().forEach(
                    (child) -> {
                        Map map = getTreeMap(child);
                        if (child.getChildren() != null) {
                            List<Map> childNext = addChildrent(child);
                            sort(childNext);
                            map.put("children", childNext);
                        }
                        if (child.getLink() != null && child.getOperations().size() > 0) {
                            List<Map> operationList = new ArrayList<>();
                            child.getOperations().forEach(
                                    (operation) -> {
                                        operationList.add(getTreeOperationsMap(operation));
                                    }
                            );
                            map.put("children", operationList);
                        }
                        list.add(map);
                    }
            );
        }
        return list;
    }

    /*
     * 完整菜单树 ，速度最快， 但得人工维护 sortCode字段
     * */
    public List<Map> getAllMenu2(SimpleRequest request) {
        // 排好序的 List<Menu>
        // 风险， sortCode 由人工维护, 出错会影响结果
        List<Menu> menusList = menuRepository.findAllByOrderBySortCode();
        // 将结果转成tree组件对应结果
        List<Map> results = new ArrayList<Map>();
        List<Integer> rootList = new ArrayList<Integer>();
        Map<Integer, Map> menus = new HashMap<>();
        menusList.forEach(
                (menu) -> {
                    if (menu.getParent() == null) {
                        menus.put(menu.getId(), getTreeMap(menu));
                        rootList.add(menu.getId());
                    } else {
                        Map children = getTreeMap(menu);
                        if (menu.getLink() != null && menu.getOperations().size() > 0) {
                            List<Map> operations = new ArrayList<Map>();
                            menu.getOperations().forEach((child) -> {
                                operations.add(getTreeOperationsMap(child));
                            });
                            children.put("children", operations);
                        }
                        menus.put(menu.getId(), children);
                        Map parent = menus.get(menu.getParent().getId());
                        List<Map> childrens = (List<Map>) parent.get("children") != null ? (List<Map>) parent.get("children") : new ArrayList<Map>();
                        childrens.add(children);
                        parent.put("children", childrens);
                    }
                }
        );
        rootList.forEach(
                (v) -> {
                    results.add(menus.get(v));
                }
        );
        return results;
    }

    public Menu getMenuForUi(SimpleRequest request) {
        return menuRepository.findAll(JpaDsl.toCriteria(request.getMap())).stream().findFirst().orElse(null);
    }


    public void updateAllPath(SimpleRequest request) {
        List<Menu> menuList = menuRepository.findAll();
        menuList.forEach(menu -> {
            menu.setPath(menu.localPath());
        });
        menuRepository.save(menuList);
    }

    public void updateAllInitials(SimpleRequest request) {
        List<Menu> menuList = menuRepository.findAll();
        menuList.forEach(menu -> {
            menu.setInitials(PinyinUtil.changeToTonePinYin(menu.getName()).replaceAll(" ", ""));
        });
        menuRepository.save(menuList);
    }


    public List<Map> getTreePath(SimpleRequest request) {
        List<Map> operations = getAllOperation(request.get("terminalTypeId"));//operationRepository.findAllData();

        Map<String, List<Map>> menuOperations = operations.stream().map(opt -> {
            Map operation = MapUtil.mapper("menu_id", Integer.parseInt(opt.get("MENU_ID").toString()),
                    "name", opt.get("NAME").toString(),
                    "operationId", opt.get("OPERATION_ID").toString(),
                    "value", opt.get("OPERATION_ID").toString(),
                    "checked", false);
            return operation;
        }).collect(groupingBy(item -> item.get("menu_id").toString()));

//        List<Map> menus = menuRepository.findTreeData();
        List<Map> menus = menuRepository.findTreeDataByTerminalTypeId(request.get("terminalTypeId"));
        //list<Map>转map  方便取值
        Map<String, Map> menuMap = menus.stream().collect(toMap(map -> map.get("id").toString(), map -> map, (k1, k2) -> k1));
        List<Map> newMenus = menus.stream().filter(item -> {
            //判断该菜单是否有效
            boolean valid = (boolean) item.get("valid");
            if (valid) {
                //填充 按钮操作 的值
                String id = item.get("id").toString();
                item.put("operations", menuOperations.get(id));
                item.put("terminal", item.get("terminal"));
                //判断父级菜单是否存在及有效
                if (item.get("parentId") != null) {
                    return isValid(menuMap, item.get("parentId").toString());
                }
                return true;
            }
            return false;
        }).collect(Collectors.toList());
        return newMenus;
    }

    private List<Map> getAllOperation(String terminalTypeId) {
        Query query = em.createNativeQuery("select  e.menu_id,e.operation_id, e.name " +
                " from aut_operations e inner join AUT_MENUS a on e.MENU_ID = a.ID where a.TERMINAL_TYPE=?" +
                " order by  case " +
                "  when e.operation_id = 'action_new' then  1 " +
                "  when e.operation_id = 'action_edit' then  2 " +
                "  when e.operation_id = 'action_delete' then 3 " +
                "  when e.operation_id = 'action_search' then 100  " +
                "  else 90 end ").setParameter(1, terminalTypeId);
        query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        List<Map> list = query.getResultList();
        return list;
    }


    /**
     * 递归判断父级菜单是否有效
     *
     * @param menuMap
     * @param parentId
     * @return
     */
    private boolean isValid(Map<String, Map> menuMap, String parentId) {
        boolean valid = (boolean) menuMap.get(parentId).get("valid");
        if (valid) {
            if (menuMap.get(parentId).get("parentId") != null) {
                return isValid(menuMap, menuMap.get(parentId).get("parentId").toString());
            }
        }
        return valid;
    }

    /**
     * 清除菜单有修改有效值的菜单验证缓存
     *
     * @param menu
     * @param oldValid
     */
    private void clearMenuAuthCache(Menu menu, Boolean oldValid) {
        if ( menu.isValid() == oldValid ) {
            return;
        }
        JSONObject param = new JSONObject();
        param.put("menuId", menu.getId());
        JSONArray roles = rbacDao.queryRoleIdByMenuId(param);
        for (Object o : roles) {
            JSONObject role = (JSONObject)o;
            param.put("roleId",  role.getString("ROLE_ID"));
            JSONArray posts = rbacDao.queryPostIdByRoleId(param);
            for (Object postO : posts) {
                JSONObject postJ = (JSONObject)postO;
                cache.remove(CONST_MENU_AUTH_CACHE, postJ.getString("POST_ID"));
            }
        }
    }

    /**
     * 功能：用于查找菜单id和菜单名称使用
     *
     * @param request
     * @param request
     * @return
     */
    public Object findAllMenu(SimpleRequest request) {

        Sort sort = new Sort(Sort.Direction.DESC, "id");
        if (request.getJO().get("condition") != null) {
            List<Map<String, String>> condition = request.get("condition");

            //从主表获取终端和分组 弹框查询时确保能统一，这两个字段前端界面已经控制必须填，才能触发
            JSONObject terminalType = (JSONObject) JSON.parseObject(request.get("conditionModel").toString()).get("terminalType");
            JSONObject groupType = (JSONObject) JSON.parseObject(request.get("conditionModel").toString()).get("groupType");
            condition.add(MapUtil.mapper("field","terminalType.id","value",terminalType.get("id"),"operator","EQ"));
            condition.add(MapUtil.mapper("field","groupType.id","value",groupType.get("id"),"operator","EQ"));

//            Map<String, String> map = new HashMap<>();
//            condition.forEach(c -> {
//                map.put(c.get("field"), c.get("value"));
//            });

            return  menuRepository.findAll(JpaDsl.toCriteria(condition), PageRequest.of(request.get("page"), request.get("size"),sort));
        }
        JSONObject jo = request.getJO().getJSONObject("group");
        JSONArray filters = jo.getJSONArray("filters");
        Boolean valueIsNumber = true;
        for (Object o : filters) {
            JSONObject filter = (JSONObject) o;
            if ("idOrName".equals(filter.get("field"))) {
                String idOrName = filter.getString("value");
                Pattern pattern = Pattern.compile("^[-\\+]?[\\d]*$");
                valueIsNumber = pattern.matcher(idOrName).matches();
            }
        }
        for (Object o : filters) {
            JSONObject filter = (JSONObject) o;
            if ("idOrName".equals(filter.get("field"))) {
                if (valueIsNumber) {
                    filter.put("field", "id");
                } else {
                    filters.remove(o);
                }

            }
        }
        return menuRepository.findAll(JSON.parseObject(jo.toString(), Group.class).build(true), PageRequest.of(request.get("page"), request.get("size"),sort));
    }

    // 设置权限明细
    public Menu findAuthItemByMenu(SimpleRequest request) {
        Menu menu = request.getO(Menu.class);
        if (menu == null) return null;
        JSONObject param = new JSONObject();
        param.put("menuId", menu.getId());
        JSONArray menuAuthItems = SpringManager.getBean(RbacDao.class).findAuthItemByMenu(param);
        List<MenuAuthItem> collect = menuAuthItems.stream().map(o -> {
            JSONObject item = (JSONObject) o;
            MenuAuthItem menuAuthItem = new MenuAuthItem();
            menuAuthItem.setMenuId(item.getString("MENU_ID"));
            menuAuthItem.setMenuName(item.getString("MENU_NAME"));
            menuAuthItem.setOperations(item.getString("OPERATIONS_NAME").replace(",",";"));
            menuAuthItem.setRoleId(item.getString("ROLE_ID"));
            menuAuthItem.setRoleName(item.getString("ROLE_NAME"));
            return menuAuthItem;
        }).collect(toList());
        menu.setMenuAuthItemList(collect);
        return menu;
    }

    /**
     * 清楚菜单所有缓存
     * @param request
     * @return
     */
    public String clearMenuCache(SimpleRequest request) {
        cache.clear(CONST_MENU_CACHE);
        cache.clear(CONST_MENU_AUTH_CACHE);
        return "清除成功";
    }
}
